/*
 * Created on Jun 24, 2005
 */
package org.vectrics.event;

import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.vectrics.SystemException;
import org.vectrics.batch.VectricsQuartzJob;
import org.vectrics.common.util.DateSync;
import org.vectrics.domain.Domain;
import org.vectrics.hibernate.ApplicationContext;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.provider.CoreServiceLocator;
import org.vectrics.recruiter.config.RecruitConfig;
import org.vectrics.user.Person;

/**
 * @author MMoore
 */
public class EventProcessor extends VectricsQuartzJob {
	public static final String BATCH_CODE = "event.proc";
	public final Logger log = Logger.getLogger(this.getClass());
	private static long runNumber = 0;
	
	public EventProcessor() {
		super();
	}
	
	protected boolean persistRunResults() {
		return(false);
	}
	
	public String getName() {
		return("Event Processor");
	}
	
	protected String getBatchTypeCode() {
		return(EventProcessor.BATCH_CODE);
	}
	
	protected boolean logOnlyErrors() {
		return(true);
	}

	protected boolean executeForEachDomain() {
		return(true);
	}
	
	public void doExecute(Domain domain, ApplicationContext applicationContext) {
		log.debug("before execute()");
		
		Person batchUser = CoreServiceLocator.getConfiguration().getBatchPerson();
		ThreadProperties.setPerson(batchUser);
		
		//execute();
		this.processAllUnprocessedEvents(domain);
		log.debug("after execute()");
	}
	
	public static long getRunNumber() {
		return(EventProcessor.runNumber);
	}
	
	protected List findUnprocessedEvents(Domain domain, int startPos, int endPos) { 
		return CoreServiceLocator.getEventService()
				.findUnprocessedEvents(domain, startPos, endPos, null);
	}
	
	
	public synchronized void processAllUnprocessedEvents(Domain domain) {
		log.debug("processAllUnprocessedEvents() called");
		boolean done = false;
		while (!done) {
			if (ThreadProperties.isTransactionInProgress())
				ThreadProperties.forcedCommit();
			
			List unprocessedEvents = this.findUnprocessedEvents(domain, 0, 10); 
			Iterator eventIter = unprocessedEvents.iterator();
			if (eventIter.hasNext() == false) {
				done = true;
			} else {
				while (eventIter.hasNext()) {
					Event event = (Event)eventIter.next();
					event = CoreServiceLocator.getEventService().findEvent(event.getId());
					if (event.getStatus().equals(Event.STATUS_UNPROCESSED)) {
						log.info("Processing event: " +
								"\r\n  -Event ID:          " + event.getId() +
								"\r\n  -Status:            " + event.getStatus() +
								"\r\n  -Retries:           " + event.getRetryCount() +
								"\r\n  -Number of attch's: " + event.getAttachments().size()
								);
						try {
							EventHandler handler = findEventHandler(event.getEventType());
							// Si esta configurado que no se notifique
							if ((RecruitConfig.getRecruitInstance().getNotificationsConfig().getNotificationsEnabled())
								&& (RecruitConfig.getRecruitInstance().getNotificationsConfig().isEnabled(event.getEventType().getCode()))) {
								
								if (ThreadProperties.isTransactionInProgress()) {
									ThreadProperties.forcedCommitAndNewTransaction();
								} else {
									ThreadProperties.beginTransaction();
								}
								
								ThreadProperties.assertTransaction();
								event = CoreServiceLocator.getEventService().findEvent(event.getId());
								
								log.debug("Handling event with: " + handler.getClass().getName());
								handler.handleEvent(event);
								
								event = CoreServiceLocator.getEventService().findEvent(event.getId());
								event.setProcessedTime(DateSync.getCurrentTime());
								event.setStatus(Event.STATUS_PROCESSED);
								event.setProcessedTime(DateSync.getCurrentTime());
								CoreServiceLocator.getEventService().updateEvent(event);
								ThreadProperties.assertTransaction();
								
								ThreadProperties.forcedCommit();
								ThreadProperties.assertTransaction();
								if (log.isDebugEnabled())
									log.debug("Event has been processed: " + event.getId());
								
								event = CoreServiceLocator.getEventService().findEvent(event.getId());
								if (false == event.getStatus().equals(Event.STATUS_PROCESSED)) {
									throw new SystemException("Event did not have status changed to processed.");
								}
								
								try {
									ThreadProperties.forcedCommit();
								} catch (Exception ex1) {
									log.error("Exception saving event information", ex1);
								}
							} else {
								// Se setea el evento como no procesado
								changeToNotProcessed(event);
							}
			
						} catch (Exception ex) {
							log.error("Exception handling event - rolling back", ex);
							try {
								ThreadProperties.rollbackTransaction();
								//ThreadProperties.closeSession();
							} catch (Exception rex) {
								log.error("Exception rolling back transaction", rex);
							}
							
							ThreadProperties.beginTransaction();
							event = CoreServiceLocator.getEventService().findEvent(event.getId());
							long retries = event.getRetryCount().longValue();
							long maxRetries = CoreServiceLocator.getConfiguration().getEventRetries(domain);
							retries++;
							event.setRetryCount(new Long(retries));
							
							if (retries > maxRetries) {
								event.setStatus(Event.STATUS_ERROR);
							}
							log.error("Exception processing event. Event id = " + event.getId()
									+ "\r\n - Number of attempts: " + event.getRetryCount()
									+ "\r\n - Event Status: " + event.getStatus()
									, ex
								);
							CoreServiceLocator.getEventService().updateEvent(event);
							ThreadProperties.forcedCommit();
						} finally {
						}
					} 
				}
			}
		}
	}
	
	// Se setea el evento como no procesado
	private void changeToNotProcessed(Event event) {
		log.error("Change event To Not Processed");
		// Se crea una transaccion
		if (ThreadProperties.isTransactionInProgress()) {
			ThreadProperties.forcedCommitAndNewTransaction();
		} else {
			ThreadProperties.beginTransaction();
		}
		event.setStatus(Event.STATUS_NOT_PROCESSED);
		CoreServiceLocator.getEventService().updateEvent(event);
		ThreadProperties.forcedCommit();		
	}

	private EventHandler findEventHandler(EventType eventType) {
		EventHandler handler = (EventHandler)CoreServiceLocator.findServiceBean(eventType.getHandlerClass());
		return(handler);
	}

}
